package BeanCircularDependency;

import lombok.RequiredArgsConstructor;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.framework.autoproxy.target.AbstractBeanFactoryBasedTargetSourceCreator;
import org.springframework.aop.target.AbstractBeanFactoryBasedTargetSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Primary;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.Order;

import javax.annotation.Priority;

import static BeanCircularDependency.BeanConfig.BeanA;

/**
 * @see PriorityOrdered
 */
@Primary
@Order(45)
@Priority(45)
// @Component
@RequiredArgsConstructor
public class SetCustomTargetSourceCreator implements BeanPostProcessor {
    final BeanFactory beanFactory;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof AnnotationAwareAspectJAutoProxyCreator) {
            AnnotationAwareAspectJAutoProxyCreator aspectJAutoProxyCreator = (AnnotationAwareAspectJAutoProxyCreator) bean;

            CustomTargetSourceCreator customTargetSourceCreator = new CustomTargetSourceCreator();
            customTargetSourceCreator.setBeanFactory(beanFactory);
            aspectJAutoProxyCreator.setCustomTargetSourceCreators(customTargetSourceCreator);
        }
        return bean;
    }

    static class CustomTargetSourceCreator extends AbstractBeanFactoryBasedTargetSourceCreator {
        @Override
        protected AbstractBeanFactoryBasedTargetSource createBeanFactoryBasedTargetSource(Class<?> beanClass, String beanName) {
            if (getBeanFactory() instanceof ConfigurableListableBeanFactory) {
                if (beanClass.isAssignableFrom(BeanA.class)) {
                    return new CustomTargetSource();
                }
            }
            return null;
        }
    }

    static class CustomTargetSource extends AbstractBeanFactoryBasedTargetSource {
        @Override
        public Object getTarget() throws Exception {
            return getBeanFactory().getBean(getTargetBeanName());
        }
    }
}
